package tc.de.krawallmann.gravoblocks.game;

import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

import tc.de.krawallmann.achievement.Achievement;
import tc.de.krawallmann.extension.AchievementExtension;
import tc.de.krawallmann.extension.FileExistenceChecker;
import tc.de.krawallmann.extension.HighscoresExtension;
import tc.de.krawallmann.extension.KeyboardInputMapExtension;
import tc.de.krawallmann.extension.SettingsExtension;
import tc.de.krawallmann.gravoblocks.achievements.Achievements;
import tc.de.krawallmann.settings.Setting;
import tc.de.krawallmann.slick.game.AbstractExtendableGame;

/**
 * The Class GravoBlocksGame.
 * @author KrawallMann
 * @version 1.0
 */
public class GravoBlocksGame extends AbstractExtendableGame {

	/** The Constant SETTINGSSTATE. */
	public static final int ACHIEVEMENTSSTATE = 6;

	/** The Constant BLINKING_GHOST. */
	public static final String BLINKING_GHOST = "blinkingGhost";

	/** The Constant ENTERSCORESSTATE. */
	public static final int ENTERSCORESSTATE = 5;

	/** The Constant GAMEPLAYSTATE. */
	public static final int GAMEPLAYSTATE = 2;
	/** The Constant GAMEPLAYSTATE. */
	public static final int PUZZLEGAMESTATE = 7;

	/** The Constant MAINMENUSTATE. */
	public static final int MAINMENUSTATE = 1;
	/** The Constant SETTINGSSTATE. */
	public static final int SETTINGSSTATE = 4;

	/** The Constant USE_SOUND. */
	public static final String USE_SOUND = "useSound";
	
	public static final String SPECIAL_TILES = "specialTiles";

	/** The Constant VIEWSCORESSTATE. */
	public static final int VIEWSCORESSTATE = 3;

	/** The Constant ACTION_KEYS. */
	private static final List<Integer> ACTION_KEYS = Arrays.asList(new Integer[] {
			Input.KEY_P, Input.KEY_LEFT, Input.KEY_RIGHT, Input.KEY_DOWN, Input.KEY_H,
			Input.KEY_UP, Input.KEY_Z, Input.KEY_ESCAPE, Input.KEY_SPACE
	});

	/** The Constant ACTIONS. */
	private static final List<String> ACTIONS = Arrays.asList(new String[] {
			"pause", "moveLeft", "moveRight", "fall", "hold", "rotateRight", "rotateLeft", "back",
			"alldown"
	});

	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 * @throws SlickException
	 *             the slick exception
	 */
	public static void main(final String[] args) throws SlickException {
		Logger.getLogger("").setLevel(Level.SEVERE);
		new GravoBlocksGame().start();
	}

	/** The achievements. */
	private transient final AchievementExtension achievements;

	/** The default keys. */
	private transient final Map<String, Integer> defaultKeys;

	/** The default settings. */
	private transient final Map<String, Setting<?>> defaultSettings;

	/**
	 * Instantiates a new gravo blocks game.
	 */
	public GravoBlocksGame() {
		super("KrawallMann's GravoBlocks");
		defaultKeys = new HashMap<String, Integer>();
		for (int i = 0; i < GravoBlocksGame.ACTIONS.size(); i++) {
			defaultKeys.put(GravoBlocksGame.ACTIONS.get(i), GravoBlocksGame.ACTION_KEYS.get(i));
		}
		defaultSettings = new HashMap<String, Setting<?>>();
		defaultSettings.put(GravoBlocksGame.USE_SOUND, new Setting<Boolean>(
				GravoBlocksGame.USE_SOUND, true));
		defaultSettings.put(GravoBlocksGame.BLINKING_GHOST, new Setting<Boolean>(
				GravoBlocksGame.BLINKING_GHOST, true));
		defaultSettings.put(GravoBlocksGame.SPECIAL_TILES, new Setting<Boolean>(
				GravoBlocksGame.SPECIAL_TILES, true));
		// Log.setVerbose(false);
		achievements = new AchievementExtension(getAchievementsFile());

		addState(new MainMenuState(GravoBlocksGame.MAINMENUSTATE, this));
		final AchievementsState astate =
				new AchievementsState(GravoBlocksGame.ACHIEVEMENTSSTATE, this);
		achievements.addAchievementsListener(astate);
		addState(astate);
		addState(new NormalGameplayState(GravoBlocksGame.GAMEPLAYSTATE, this));
		addState(new SettingsState(GravoBlocksGame.SETTINGSSTATE, this));
		addState(new ScoresState(GravoBlocksGame.VIEWSCORESSTATE, this));
		addState(new EnterScoreState(GravoBlocksGame.ENTERSCORESSTATE, this));
		addState(new PuzzleGameplayState(GravoBlocksGame.PUZZLEGAMESTATE, this));
		enterState(GravoBlocksGame.MAINMENUSTATE);
	}

	/**
	 * Gets the achievements file.
	 * 
	 * @return the achievements file
	 */
	public final String getAchievementsFile() {
		return getGameDirectory() + File.separator + "achievements.xml";
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractGame#getActions()
	 */
	@Override
	public List<String> getActions() {
		return GravoBlocksGame.ACTIONS;
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractGame#getDefaultActionKeys()
	 */
	@Override
	public List<Integer> getDefaultActionKeys() {
		return GravoBlocksGame.ACTION_KEYS;
	}

	/**
	 * Gets the game directory.
	 * 
	 * @return the game directory
	 */
	public final String getGameDirectory() {
		return getUserHome() + File.separator + "KMSoftware" + File.separator + "GravoBlocks3";
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractGame#getIcon16x16()
	 */
	@Override
	public final String getIcon16x16() {
		return "tc/de/krawallmann/gravoblocks/resource/logo16x16.png";
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractGame#getIcon24x24()
	 */
	@Override
	public final String getIcon24x24() {
		return "tc/de/krawallmann/gravoblocks/resource/logo24x24.png";
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractGame#getIcon32x32()
	 */
	@Override
	public final String getIcon32x32() {
		return "tc/de/krawallmann/gravoblocks/resource/logo32x32.png";
	}

	/**
	 * Gets the input file.
	 * 
	 * @return the input file
	 */
	public final String getInputFile() {
		return getGameDirectory() + File.separator + "input.xml";
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractExtendableGame#getPossibleAchievements()
	 */
	@Override
	public List<Achievement> getPossibleAchievements() {
		return Arrays.asList(new Achievement[] {
				Achievements.GBWORKS, Achievements.FIRSTGAME, Achievements.USEDHOLD,
				Achievements.TENLINES, Achievements.FIFTYLINES, Achievements.LINES100,
				Achievements.LINES150, Achievements.LINES250, Achievements.FIVEMINUTES,
				Achievements.TENMINUTES, Achievements.FIFTEENMINUTES,Achievements.TWENTYMINUTES,
				Achievements.FOURROWS,Achievements.FOURROWS2,Achievements.FOURROWS3,Achievements.FOURROWS4,
				Achievements.FOURROWSINLINE2,Achievements.FOURROWSINLINE3,Achievements.FOURROWSINLINE4,
				Achievements.FOURROWSINLINE5
		});
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractGame#getReleaseDate()
	 */
	@Override
	public long getReleaseDate() {
		return 1310390369946L;
	}

	/**
	 * Gets the scores file.
	 * 
	 * @return the scores file
	 */
	public final String getScoresFile() {
		return getGameDirectory() + File.separator + "scores.xml";
	}

	/**
	 * Gets the settings file.
	 * 
	 * @return the settings file
	 */
	public final String getSettingsFile() {
		return getGameDirectory() + File.separator + "settings.xml";
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractExtendableGame#getVersion()
	 */
	@Override
	public String getVersion() {
		return "Version 3.2.3";
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * org.newdawn.slick.state.StateBasedGame#initStatesList(org.newdawn.slick
	 * .GameContainer)
	 */
	@Override
	public void initStatesList(final GameContainer container) throws SlickException {
		getState(GravoBlocksGame.MAINMENUSTATE).init(container, this);
	}

	/* (non-Javadoc)
	 * @see tc.de.krawallmann.slick.game.AbstractExtendableGame#registerExtensions()
	 */
	@Override
	public void registerExtensions() {
		registerExtension(new FileExistenceChecker(getGameDirectory()));
		registerExtension(new KeyboardInputMapExtension(getInputFile(), defaultKeys));
		registerExtension(new HighscoresExtension(getScoresFile()));
		registerExtension(new SettingsExtension(getSettingsFile(), defaultSettings));
		registerExtension(achievements);
	}

	/**
	 * Start.
	 * 
	 * @throws SlickException
	 *             the slick exception
	 */
	public void start() throws SlickException {
		start(800, 600, 60);
	}

	/**
	 * Start.
	 * 
	 * @param width
	 *            the width
	 * @param height
	 *            the height
	 * @throws SlickException
	 *             the slick exception
	 */
	public void start(final int width, final int height) throws SlickException {
		start(width, height, 60);
	}

	/**
	 * Start.
	 * 
	 * @param width
	 *            the width
	 * @param height
	 *            the height
	 * @param fps
	 *            the fps
	 * @throws SlickException
	 *             the slick exception
	 */
	public void start(final int width, final int height, final int fps) throws SlickException {
		final AppGameContainer container = new AppGameContainer(this);
		if (container instanceof AppGameContainer) {
			container.setIcons(new String[] {
					getIcon32x32(), getIcon24x24(), getIcon16x16()
			});
		}
		container.setTargetFrameRate(fps);
		container.setShowFPS(false);
		container.setDisplayMode(width, height, false);
		container.setClearEachFrame(false);
		container.start();
	}

	/*
	 * (non-Javadoc)
	 * @see tc.de.krawallmann.twl.AbstractTWLStateBasedGame#getThemeURL()
	 */
	@Override
	protected URL getThemeURL() {
		return getClass().getResource("/tc/de/krawallmann/gravoblocks/resource/myTheme.xml");
	}
}
